<template>
  <div class="container">
    <div class="progress" aria-hidden="true">
      <div class="progress-bar" :style="{ width: progressPercent + '%' }"></div>
    </div>
    <RouterLink class="back" :to="{ name: 'home' }">← 返回</RouterLink>

    <div class="post-grid" v-if="post">
      <article ref="articleEl">
        <h1 class="title">{{ post.title }}</h1>
        <div class="meta">
          <span v-if="post.date" class="date">
            <el-icon style="vertical-align: -2px; margin-right: 4px;"><calendar /></el-icon>{{ post.date }}
          </span>
          <span class="sep">·</span>
          <span class="readtime">约 {{ readingMinutes }} 分钟读完</span>
          <span v-if="post.tags?.length" class="tags">
            <el-tag v-for="t in post.tags" :key="t" size="small" effect="dark" type="success">{{ t }}</el-tag>
          </span>
        </div>
        <div ref="contentEl" class="content" v-html="post.html" />
        <el-divider class="divider" />

        <el-card class="section-card" shadow="hover" v-if="related.length">
          <template #header>
            <div class="card-header"><span>相关推荐</span></div>
          </template>
          <el-row :gutter="12">
            <el-col v-for="r in related" :key="r.slug" :xs="24" :sm="12">
              <RouterLink :to="{ name: 'post', params: { slug: r.slug } }">
                <el-card class="subcard" shadow="never">
                  {{ r.title }}
                </el-card>
              </RouterLink>
            </el-col>
          </el-row>
        </el-card>

        <el-card class="section-card" shadow="hover" style="margin-top:12px;">
          <template #header>
            <div class="card-header"><span>继续探索</span></div>
          </template>
          <div class="cta">
            <RouterLink to="/blog"><el-button type="primary" plain round>查看更多文章</el-button></RouterLink>
            <RouterLink to="/about"><el-button plain round>关注/联系</el-button></RouterLink>
            <el-button plain round @click="copyLink">复制本文链接</el-button>
          </div>
        </el-card>

        <div class="post-nav">
          <RouterLink v-if="prevMeta" :to="{ name: 'post', params: { slug: prevMeta.slug } }">
            <el-button link type="success">上一篇：{{ prevMeta.title }}</el-button>
          </RouterLink>
          <span></span>
          <RouterLink v-if="nextMeta" :to="{ name: 'post', params: { slug: nextMeta.slug } }">
            <el-button link type="success">下一篇：{{ nextMeta.title }}</el-button>
          </RouterLink>
        </div>
      </article>

      <aside class="toc" v-if="tocItems.length">
        <div class="toc-title">目录</div>
        <ul class="toc-list">
          <li v-for="i in tocItems" :key="i.id" :class="['toc-item', 'lv' + i.level, { active: i.id === activeTocId }]">
            <a href="#" @click.prevent="scrollTo(i.id)">{{ i.text }}</a>
          </li>
        </ul>
      </aside>
    </div>

    <p v-else class="muted">加载中...</p>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref, watch, computed, nextTick, onBeforeUnmount } from 'vue';
import { useRoute } from 'vue-router';
import { usePostsStore, type PostDetail, type PostMeta } from '@/stores/posts';
import { Calendar as calendar } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';

const route = useRoute();
const store = usePostsStore();
const post = ref<PostDetail | null>(null);
const metas = computed(() => store.metas);
const slug = computed(() => String(route.params.slug));

const currentIndex = computed(() => metas.value.findIndex(m => m.slug === slug.value));
const prevMeta = computed<PostMeta | null>(() => currentIndex.value > 0 ? metas.value[currentIndex.value - 1] : null);
const nextMeta = computed<PostMeta | null>(() => currentIndex.value >= 0 && currentIndex.value < metas.value.length - 1 ? metas.value[currentIndex.value + 1] : null);

const articleEl = ref<HTMLElement | null>(null);
const contentEl = ref<HTMLElement | null>(null);
const progressPercent = ref(0);
const tocItems = ref<Array<{ id: string; text: string; level: number }>>([]);
const activeTocId = ref('');

const readingMinutes = computed(() => {
  const html = post.value?.html || '';
  const temp = document.createElement('div');
  temp.innerHTML = html;
  const text = (temp.textContent || '').replace(/\s+/g, '');
  const minutes = Math.max(1, Math.ceil(text.length / 500));
  return minutes;
});

function slugify(text: string): string {
  return text
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9\u4e00-\u9fa5\s-]/g, '')
    .replace(/\s+/g, '-')
    .slice(0, 60);
}

function buildToc() {
  tocItems.value = [];
  if (!contentEl.value) return;
  const headings = Array.from(contentEl.value.querySelectorAll('h2, h3')) as HTMLHeadingElement[];
  const used = new Set<string>();
  headings.forEach(h => {
    let id = h.id || slugify(h.textContent || '');
    let base = id;
    let n = 1;
    while (!id || used.has(id)) {
      id = base + '-' + n++;
    }
    h.id = id;
    used.add(id);
    tocItems.value.push({ id, text: h.textContent || '', level: h.tagName === 'H2' ? 2 : 3 });
  });
}

function updateProgress() {
  if (!articleEl.value) return;
  const start = articleEl.value.getBoundingClientRect().top + window.scrollY;
  const end = start + articleEl.value.offsetHeight - window.innerHeight;
  const p = ((window.scrollY - start) / Math.max(1, end - start)) * 100;
  progressPercent.value = Math.max(0, Math.min(100, Math.round(p)));

  // active toc
  const ids = tocItems.value.map(i => i.id);
  let current = '';
  for (const id of ids) {
    const el = document.getElementById(id);
    if (!el) continue;
    const y = el.getBoundingClientRect().top;
    if (y <= 100) current = id; else break;
  }
  if (current) activeTocId.value = current;
}

function scrollTo(id: string) {
  const el = document.getElementById(id);
  if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
}

const related = computed<PostMeta[]>(() => {
  if (!post.value) return [];
  const current = post.value;
  const currentTags = new Set(current.tags || []);
  const scored = metas.value
    .filter(m => m.slug !== current.slug)
    .map(m => ({
      meta: m,
      score: (m.tags || []).reduce((s, t) => s + (currentTags.has(t) ? 1 : 0), 0)
    }));
  scored.sort((a, b) => {
    if (b.score !== a.score) return b.score - a.score;
    const da = a.meta.date || '';
    const db = b.meta.date || '';
    return db.localeCompare(da);
  });
  let list = scored.filter(s => s.score > 0).map(s => s.meta);
  if (list.length < 6) {
    const rest = metas.value.filter(m => m.slug !== current.slug && !list.includes(m));
    list = list.concat(rest.slice(0, 6 - list.length));
  }
  return list.slice(0, 6);
});

function copyLink() {
  const url = window.location.href;
  if (navigator.clipboard?.writeText) {
    navigator.clipboard.writeText(url).then(() => ElMessage.success('链接已复制'));
  } else {
    const textarea = document.createElement('textarea');
    textarea.value = url; document.body.appendChild(textarea); textarea.select();
    try { document.execCommand('copy'); ElMessage.success('链接已复制'); } finally { document.body.removeChild(textarea); }
  }
}

async function load() {
  if (!store.metas.length) {
    await store.loadMetas();
  }
  post.value = await store.getPost(slug.value);
  await nextTick();
  buildToc();
  updateProgress();
  enhanceCodeBlocks();
}

onMounted(load);
watch(() => route.params.slug, load);
onMounted(() => window.addEventListener('scroll', updateProgress, { passive: true }));
onBeforeUnmount(() => window.removeEventListener('scroll', updateProgress));

function enhanceCodeBlocks() {
  if (!contentEl.value) return;
  const pres = Array.from(contentEl.value.querySelectorAll('pre')) as HTMLPreElement[];
  pres.forEach(pre => {
    if ((pre as any)._enhanced) return;
    (pre as any)._enhanced = true;

    const code = pre.querySelector('code');
    const langClass = Array.from(code?.classList || []).find(c => c.startsWith('language-'));
    const lang = langClass ? langClass.replace('language-', '').toUpperCase() : 'CODE';

    const wrapper = document.createElement('div');
    wrapper.className = 'code-wrapper';
    pre.parentNode?.insertBefore(wrapper, pre);
    wrapper.appendChild(pre);

    const toolbar = document.createElement('div');
    toolbar.className = 'code-toolbar';
    const langSpan = document.createElement('span');
    langSpan.className = 'code-lang';
    langSpan.textContent = lang;
    const copyBtn = document.createElement('button');
    copyBtn.className = 'code-copy';
    copyBtn.type = 'button';
    copyBtn.textContent = '复制';
    copyBtn.addEventListener('click', () => {
      const text = code?.textContent || '';
      if (navigator.clipboard?.writeText) {
        navigator.clipboard.writeText(text).then(() => ElMessage.success('代码已复制'));
      } else {
        const textarea = document.createElement('textarea');
        textarea.value = text; document.body.appendChild(textarea); textarea.select();
        try { document.execCommand('copy'); ElMessage.success('代码已复制'); } finally { document.body.removeChild(textarea); }
      }
    });

    const lines = (code?.textContent || '').split('\n').length;
    let toggleBtn: HTMLButtonElement | null = null;
    if (lines > 20 || pre.scrollHeight > 360) {
      wrapper.classList.add('collapsed');
      toggleBtn = document.createElement('button');
      toggleBtn.className = 'code-toggle';
      toggleBtn.type = 'button';
      toggleBtn.textContent = '展开代码';
      toggleBtn.addEventListener('click', () => {
        const isCollapsed = wrapper.classList.toggle('collapsed');
        toggleBtn!.textContent = isCollapsed ? '展开代码' : '收起代码';
      });
    }

    toolbar.appendChild(langSpan);
    toolbar.appendChild(document.createElement('span')).className = 'spacer';
    if (toggleBtn) toolbar.appendChild(toggleBtn);
    toolbar.appendChild(copyBtn);
    wrapper.insertBefore(toolbar, pre);
  });
}
</script>

<style scoped>
.container { max-width: 1024px; margin: 0 auto; padding: 2rem; }
.progress { position: fixed; left: 0; top: 0; right: 0; height: 3px; background: transparent; z-index: 100; }
.progress-bar { height: 100%; background: var(--accent); width: 0; transition: width .1s linear; }
.back { display: inline-block; margin-bottom: 1rem; color: var(--gray); font-weight: 500; }
.back:hover { color: var(--primary); }
.title { 
  margin: .25rem 0 0.25rem; 
  font-size: 2rem;
  font-family: 'Orbitron', monospace;
  color: var(--light);
  background: linear-gradient(135deg, var(--light) 0%, var(--gray) 100%);
  -webkit-background-clip: text;
  background-clip: text;
}
.meta { display: flex; align-items: center; gap: .5rem; color: var(--gray); margin-bottom: 1rem; flex-wrap: wrap; }
.meta .sep { opacity: .6; }
.meta .tags { display: inline-flex; gap: 6px; }
.divider { --el-border-color: var(--glass-border); }
.content :deep(img) { max-width: 100%; border-radius: 8px; }
.content { 
  font-size: 1.02rem; 
  line-height: 1.85; 
  color: var(--light);
}
.content :deep(p) { margin: 0 0 1.1rem; color: var(--light); }
.content :deep(ul), .content :deep(ol) { 
  padding-left: 1.2rem; 
  margin: 0 0 1rem;
  color: var(--light);
}
.content :deep(li) { margin: .25rem 0; color: var(--light); }
.content :deep(pre) {
  background: rgba(15, 23, 42, 0.8); 
  color: var(--light); 
  padding: 1rem; 
  border-radius: 10px; 
  overflow: auto;
  border: 1px solid var(--glass-border);
  backdrop-filter: blur(10px);
}
.content :deep(code) { 
  color: var(--primary);
  background: rgba(37, 99, 235, 0.1);
  border: 1px solid rgba(37, 99, 235, 0.2);
}
.content :deep(a) { color: var(--primary); }
.content :deep(a:hover) { color: var(--secondary); }
.content :deep(h2), .content :deep(h3) { 
  margin-top: 1.6rem; 
  scroll-margin-top: 72px; 
  color: var(--light);
  font-family: 'Orbitron', monospace;
}
.content :deep(h2) { 
  padding-left: .6rem; 
  border-left: 3px solid var(--primary);
  background: linear-gradient(135deg, var(--light) 0%, var(--gray) 100%);
  -webkit-background-clip: text;
  background-clip: text;
}
.content :deep(h3) {
  color: var(--gray);
}
.content :deep(blockquote) {
  margin: 1rem 0; 
  padding: .6rem .9rem; 
  border-left: 3px solid var(--primary);
  background: var(--glass); 
  color: var(--light); 
  border-radius: 8px; 
  border: 1px solid var(--glass-border);
  backdrop-filter: blur(10px);
}
.content :deep(hr) { 
  border: none; 
  height: 1px; 
  background: linear-gradient(90deg, transparent, var(--glass-border), transparent); 
  margin: 1.5rem 0; 
}
.content :deep(table) { 
  width: 100%; 
  border-collapse: collapse; 
  margin: 1rem 0; 
  font-size: .98rem;
  background: var(--glass);
  border-radius: 8px;
  overflow: hidden;
}
.content :deep(th), .content :deep(td) { 
  border: 1px solid var(--glass-border); 
  padding: .5rem .6rem;
  color: var(--light);
}
.content :deep(th) { 
  background: rgba(37, 99, 235, 0.1); 
  text-align: left;
  color: var(--primary);
  font-weight: 600;
}

.post-nav { display: flex; justify-content: space-between; align-items: center; margin-top: .5rem; }

.section-card { 
  background: var(--glass); 
  border: 1px solid var(--glass-border);
  backdrop-filter: blur(15px);
  transition: all 0.3s ease;
}

.section-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
}

.subcard { 
  background: var(--glass); 
  border: 1px solid var(--glass-border);
  backdrop-filter: blur(10px);
  transition: all 0.3s ease;
}

.subcard:hover {
  background: rgba(37, 99, 235, 0.05);
  border-color: var(--primary);
  transform: translateX(5px);
}
.card-header { display: flex; align-items: center; justify-content: space-between; }
.cta { display: flex; gap: .5rem; flex-wrap: wrap; }

.post-grid { display: block; }
@media (min-width: 1024px) {
  .post-grid { display: grid; grid-template-columns: 1fr 260px; gap: 24px; align-items: start; }
  .toc { position: sticky; top: 72px; max-height: calc(100vh - 96px); overflow: auto; }
}
.toc-title { 
  font-weight: 700; 
  margin: .5rem 0 0.5rem;
  color: var(--light);
  font-family: 'Orbitron', monospace;
}
.toc-list { list-style: none; padding: 0; margin: 0; }
.toc-item { margin: .25rem 0; }
.toc-item.lv3 { margin-left: 12px; }
.toc-item a { 
  color: var(--gray); 
  text-decoration: none;
  transition: all 0.3s ease;
  padding: 4px 8px;
  border-radius: 4px;
  display: block;
}
.toc-item a:hover {
  color: var(--primary);
  background: rgba(37, 99, 235, 0.1);
}
.toc-item.active a { 
  color: var(--primary);
  background: rgba(37, 99, 235, 0.1);
  border-left: 2px solid var(--primary);
  padding-left: 12px;
}

.code-wrapper { 
  position: relative; 
  border-radius: 10px; 
  border: 1px solid var(--glass-border); 
  background: var(--glass);
  backdrop-filter: blur(10px);
}
.code-wrapper > pre { margin: 0; max-height: 360px; }
.code-wrapper.collapsed > pre { max-height: 200px; }
.code-toolbar { 
  position: absolute; 
  top: 6px; 
  right: 8px; 
  left: 8px; 
  display: flex; 
  align-items: center; 
  gap: 8px;
  z-index: 10;
}
.code-toolbar .spacer { flex: 1; }
.code-lang { 
  font-size: .75rem; 
  color: var(--primary); 
  background: rgba(37, 99, 235, 0.1); 
  border-radius: 6px; 
  padding: 2px 6px;
  border: 1px solid rgba(37, 99, 235, 0.2);
}
.code-copy, .code-toggle { 
  font-size: .75rem; 
  padding: 4px 8px; 
  border-radius: 6px; 
  border: 1px solid var(--glass-border); 
  background: var(--glass); 
  color: var(--light); 
  cursor: pointer;
  transition: all 0.3s ease;
  backdrop-filter: blur(10px);
}
.code-copy:hover, .code-toggle:hover { 
  background: rgba(37, 99, 235, 0.1);
  border-color: var(--primary);
  color: var(--primary);
}
</style>

